Load Libraries
packages <- c(
'ggplot2','tidyverse','plotly','leaflet',
'shiny','shinyWidgets','shinydashboard',
'xts','forecast','TTR',
'DT','lubridate','RColorBrewer','scales','stopwords',
'tidytext','stringr','wordcloud','wordcloud2',
'SnowballC','textmineR','topicmodels','textclean','tm'
)
for (package in packages) {
if (!require(package, character.only = T, quietly = T)) {
install.packages(package)
library(package, character.only = T)
}
}
Load data
crm <- read_csv("CRM_interacions_table.csv")
gift <- read_csv("gift_transactions_table.csv")
video <- read_csv("video_email_data_table.csv")
constituent <- read_csv("constituent_profiles_table.csv")
Part 1: The Untapped Potential: Understanding Our Donor
Landscape
CRM Data Overview
# CRM Interaction Type
g <- crm %>%
group_by(CRM_INTERACTION_TYPE) %>%
summarise(Total = n()) %>%
select(CRM_INTERACTION_TYPE, Total) %>%
ggplot(aes(x = reorder(CRM_INTERACTION_TYPE,Total) ,y = Total)) +
geom_bar(stat = "identity",width = 0.5, fill='black') +
scale_y_continuous(labels = scales::comma) +
labs(x ="CRM Interaction Type", y = "Count") + coord_flip() +
theme(legend.text = element_text(size = 12),
legend.title = element_text(size = 12),
axis.title = element_text(size = 14),
axis.text = element_text(size = 12))
ggplotly(g)
CRM Interaction Over Time
crm <- crm %>%
mutate(Year = lubridate::year(CRM_INTERACTION_DATE),
Quarter = lubridate::quarter(CRM_INTERACTION_DATE),
Month = lubridate::month(CRM_INTERACTION_DATE, label = TRUE),
DOW = lubridate::wday(CRM_INTERACTION_DATE, label=TRUE))
CRM Interaction by Year
crm_year <- crm %>%
group_by(Year, CRM_INTERACTION_TYPE) %>%
summarise(Total = n()) %>%
select(Year,CRM_INTERACTION_TYPE, Total)
g <- ggplot(crm_year, aes(as.factor(Year), Total, group=CRM_INTERACTION_TYPE, colour = CRM_INTERACTION_TYPE)) +
geom_line( linewidth=1) + theme_minimal() +
labs(x = "Year", y = "Total", color="CRM Interaction Type") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)
CRM Interaction by Quarter
crm %>%
group_by(Quarter, CRM_INTERACTION_TYPE) %>%
summarise(Total = n()) %>%
select(Quarter,CRM_INTERACTION_TYPE, Total) %>%
ggplot(aes(as.factor(Quarter), Total, group=CRM_INTERACTION_TYPE, colour = CRM_INTERACTION_TYPE)) +
geom_line( linewidth=1) + theme_minimal() +
labs(x = "Quarter", y = "Total", color="CRM Interaction Type") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 0, hjust = 1))

CRM Interaction by Month
crm %>%
group_by(Month, CRM_INTERACTION_TYPE) %>%
summarise(Total = n()) %>%
select(Month,CRM_INTERACTION_TYPE, Total) %>%
ggplot(aes(as.factor(Month), Total, group=CRM_INTERACTION_TYPE, colour = CRM_INTERACTION_TYPE)) +
geom_line( linewidth=1) + theme_minimal() +
labs(x = "Quarter", y = "Total", color="CRM Interaction Type") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 0, hjust = 1))

CRM Interaction by Day of Week
crm %>%
group_by(DOW, CRM_INTERACTION_TYPE) %>%
summarise(Total = n()) %>%
select(DOW,CRM_INTERACTION_TYPE, Total) %>%
ggplot(aes(as.factor(DOW), Total, group=CRM_INTERACTION_TYPE, colour = CRM_INTERACTION_TYPE)) +
geom_line( linewidth=1) + theme_minimal() +
labs(x = "Day of Week", y = "Total", color="CRM Interaction Type") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 0, hjust = 1))

Gift Overview
Gifts overtime
gift <- gift %>%
mutate(Year = lubridate::year(GIFT_DATE),
Quarter = lubridate::quarter(GIFT_DATE),
Month = lubridate::month(GIFT_DATE, label = TRUE),
DOW = lubridate::wday(GIFT_DATE, label=TRUE))
Gift by Year
g <- gift %>%
group_by(Year) %>%
summarise(Total = sum(AMOUNT)) %>%
select(Year, Total) %>%
na.omit() %>%
ggplot(aes(Year, Total)) +
geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
labs(x = "Year", y = "Total") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 90, hjust = 1))
ggplotly(g)
Gift by Quarter
g <- gift %>%
group_by(Quarter) %>%
summarise(Total = sum(AMOUNT)) %>%
select(Quarter, Total) %>%
na.omit() %>%
ggplot(aes(Quarter, Total)) +
geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
labs(x = "Quarter", y = "Total") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)
Gift by Month
g <- gift %>%
group_by(Month) %>%
summarise(Total = sum(AMOUNT)) %>%
select(Month, Total) %>%
na.omit() %>%
ggplot(aes(Month, Total)) +
geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
labs(x = "Month", y = "Total") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)
Gift by Day of Week
g <- gift %>%
group_by(DOW) %>%
summarise(Total = sum(AMOUNT)) %>%
select(DOW, Total) %>%
na.omit() %>%
ggplot(aes(DOW, Total)) +
geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
labs(x = "Day of Week", y = "Total") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)
Video Overview
Video Views over time
video <- video %>%
mutate(Year = lubridate::year(SENT_DATE),
Quarter = lubridate::quarter(SENT_DATE),
Month = lubridate::month(SENT_DATE, label = TRUE),
DOW = lubridate::wday(SENT_DATE, label=TRUE))
Video views by year
g <- video %>%
group_by(Year) %>%
summarise(Total = sum(VIDEO_VIEWS)) %>%
select(Year, Total) %>%
na.omit() %>%
ggplot(aes(as.factor(Year), Total)) +
geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
labs(x = "Year", y = "Total") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)
Video Views by Quarter
g <- video %>%
group_by(Quarter) %>%
summarise(Total = sum(VIDEO_VIEWS)) %>%
select(Quarter, Total) %>%
na.omit() %>%
ggplot(aes(Quarter, Total)) +
geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
labs(x = "Quarter", y = "Total") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)
Video Views by Month
g <- video %>%
group_by(Month) %>%
summarise(Total = sum(VIDEO_VIEWS)) %>%
select(Month, Total) %>%
na.omit() %>%
ggplot(aes(Month, Total)) +
geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
labs(x = "Month", y = "Total") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)
Video Views by Day of Week
g <- video %>%
group_by(DOW) %>%
summarise(Total = sum(VIDEO_VIEWS)) %>%
select(DOW, Total) %>%
na.omit() %>%
ggplot(aes(DOW, Total)) +
geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
labs(x = "Day of the week", y = "Total") +
scale_y_continuous(labels = comma) +
theme(legend.text = element_text(size = 10),
legend.title = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 10),
axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)
Donor & Engagement Insights
Donations by CRM Interaction Type
left_join(gift,crm,by='CONSTITUENT_ID') %>%
group_by(CRM_INTERACTION_TYPE) %>%
summarise(Total = sum(AMOUNT)) %>%
select(CRM_INTERACTION_TYPE,Total) %>%
ggplot(aes(x = reorder(CRM_INTERACTION_TYPE,Total) ,y = Total)) +
geom_bar(stat = "identity",width = 0.5, fill='black') +
scale_y_continuous(labels = scales::comma) +
labs(x ="CRM Interaction Type", y = "Donations") + coord_flip() +
theme(legend.text = element_text(size = 12),
legend.title = element_text(size = 12),
axis.title = element_text(size = 14),
axis.text = element_text(size = 12))

NA
NA
Part 2: A Tale of Portfolios/Customer Segmentation
```
Part 3: The Path Forward: Activating Our Strategy
LS0tCnRpdGxlOiAiQXByYSBEYXRhIFNjaWVuY2UgQ2hhbGxlbmdlIDIwMjUiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgTG9hZCBMaWJyYXJpZXMKYGBge3J9CgpwYWNrYWdlcyA8LSBjKAogICdnZ3Bsb3QyJywndGlkeXZlcnNlJywncGxvdGx5JywnbGVhZmxldCcsCiAgJ3NoaW55Jywnc2hpbnlXaWRnZXRzJywnc2hpbnlkYXNoYm9hcmQnLAogICd4dHMnLCdmb3JlY2FzdCcsJ1RUUicsCiAgJ0RUJywnbHVicmlkYXRlJywnUkNvbG9yQnJld2VyJywnc2NhbGVzJywnc3RvcHdvcmRzJywKICAndGlkeXRleHQnLCdzdHJpbmdyJywnd29yZGNsb3VkJywnd29yZGNsb3VkMicsCiAgJ1Nub3diYWxsQycsJ3RleHRtaW5lUicsJ3RvcGljbW9kZWxzJywndGV4dGNsZWFuJywndG0nCikKZm9yIChwYWNrYWdlIGluIHBhY2thZ2VzKSB7IAogIGlmICghcmVxdWlyZShwYWNrYWdlLCBjaGFyYWN0ZXIub25seSA9IFQsIHF1aWV0bHkgPSBUKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlKQogICAgbGlicmFyeShwYWNrYWdlLCBjaGFyYWN0ZXIub25seSA9IFQpCiAgfQp9CmBgYAoKIyBMb2FkIGRhdGEKYGBge3J9CmNybSA8LSByZWFkX2NzdigiQ1JNX2ludGVyYWNpb25zX3RhYmxlLmNzdiIpCmdpZnQgPC0gcmVhZF9jc3YoImdpZnRfdHJhbnNhY3Rpb25zX3RhYmxlLmNzdiIpCnZpZGVvIDwtIHJlYWRfY3N2KCJ2aWRlb19lbWFpbF9kYXRhX3RhYmxlLmNzdiIpCmNvbnN0aXR1ZW50IDwtIHJlYWRfY3N2KCJjb25zdGl0dWVudF9wcm9maWxlc190YWJsZS5jc3YiKQpgYGAKCiMgUGFydCAxOiBUaGUgVW50YXBwZWQgUG90ZW50aWFsOiBVbmRlcnN0YW5kaW5nIE91ciBEb25vciBMYW5kc2NhcGUKCiMjIENSTSBEYXRhIE92ZXJ2aWV3CmBgYHtyfQojIENSTSBJbnRlcmFjdGlvbiBUeXBlCmcgPC0gY3JtICU+JQogICAgICAgIGdyb3VwX2J5KENSTV9JTlRFUkFDVElPTl9UWVBFKSAlPiUKICAgICAgICBzdW1tYXJpc2UoVG90YWwgPSBuKCkpICU+JQogICAgICAgIHNlbGVjdChDUk1fSU5URVJBQ1RJT05fVFlQRSwgVG90YWwpICU+JQogICAgICAgIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoQ1JNX0lOVEVSQUNUSU9OX1RZUEUsVG90YWwpICx5ID0gVG90YWwpKSAgKwogICAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLHdpZHRoID0gMC41LCBmaWxsPSdibGFjaycpICArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpICsKICAgICAgICBsYWJzKHggPSJDUk0gSW50ZXJhY3Rpb24gVHlwZSIsIHkgPSAiQ291bnQiKSArIGNvb3JkX2ZsaXAoKSArCiAgICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpCiAgICAgIApnZ3Bsb3RseShnKQpgYGAKCgojIyMgQ1JNIEludGVyYWN0aW9uIE92ZXIgVGltZQpgYGB7cn0KY3JtIDwtIGNybSAlPiUKICAgICAgICBtdXRhdGUoWWVhciA9IGx1YnJpZGF0ZTo6eWVhcihDUk1fSU5URVJBQ1RJT05fREFURSksCiAgICAgICAgICAgICAgIFF1YXJ0ZXIgPSBsdWJyaWRhdGU6OnF1YXJ0ZXIoQ1JNX0lOVEVSQUNUSU9OX0RBVEUpLAogICAgICAgICAgICAgICBNb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoQ1JNX0lOVEVSQUNUSU9OX0RBVEUsIGxhYmVsID0gVFJVRSksCiAgICAgICAgICAgICAgIERPVyA9IGx1YnJpZGF0ZTo6d2RheShDUk1fSU5URVJBQ1RJT05fREFURSwgbGFiZWw9VFJVRSkpCmBgYAoKIyMjIyBDUk0gSW50ZXJhY3Rpb24gYnkgWWVhcgpgYGB7cn0KY3JtX3llYXIgPC0gY3JtICU+JQpncm91cF9ieShZZWFyLCBDUk1fSU5URVJBQ1RJT05fVFlQRSkgJT4lCiAgICAgICAgc3VtbWFyaXNlKFRvdGFsID0gbigpKSAlPiUKICAgICAgICBzZWxlY3QoWWVhcixDUk1fSU5URVJBQ1RJT05fVFlQRSwgVG90YWwpCgogICBnIDwtIGdncGxvdChjcm1feWVhciwgYWVzKGFzLmZhY3RvcihZZWFyKSwgVG90YWwsIGdyb3VwPUNSTV9JTlRFUkFDVElPTl9UWVBFLCBjb2xvdXIgPSBDUk1fSU5URVJBQ1RJT05fVFlQRSkpICsgCiAgICAgIGdlb21fbGluZSggbGluZXdpZHRoPTEpICsgdGhlbWVfbWluaW1hbCgpICsKICAgICAgbGFicyh4ID0gIlllYXIiLCB5ID0gIlRvdGFsIiwgY29sb3I9IkNSTSBJbnRlcmFjdGlvbiBUeXBlIikgKyAKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpCmdncGxvdGx5KGcpCmBgYAoKIyMjIyBDUk0gSW50ZXJhY3Rpb24gYnkgUXVhcnRlcgpgYGB7cn0KY3JtICU+JQpncm91cF9ieShRdWFydGVyLCBDUk1fSU5URVJBQ1RJT05fVFlQRSkgJT4lCiAgICAgICAgc3VtbWFyaXNlKFRvdGFsID0gbigpKSAlPiUKICAgICAgICBzZWxlY3QoUXVhcnRlcixDUk1fSU5URVJBQ1RJT05fVFlQRSwgVG90YWwpICU+JSAKICAgICAgZ2dwbG90KGFlcyhhcy5mYWN0b3IoUXVhcnRlciksIFRvdGFsLCBncm91cD1DUk1fSU5URVJBQ1RJT05fVFlQRSwgY29sb3VyID0gQ1JNX0lOVEVSQUNUSU9OX1RZUEUpKSArIAogICAgICBnZW9tX2xpbmUoIGxpbmV3aWR0aD0xKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJRdWFydGVyIiwgeSA9ICJUb3RhbCIsIGNvbG9yPSJDUk0gSW50ZXJhY3Rpb24gVHlwZSIpICsgCiAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogICAgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEpKQoKYGBgCgojIyMjIENSTSBJbnRlcmFjdGlvbiBieSBNb250aApgYGB7cn0KY3JtICU+JQpncm91cF9ieShNb250aCwgQ1JNX0lOVEVSQUNUSU9OX1RZUEUpICU+JQogICAgICAgIHN1bW1hcmlzZShUb3RhbCA9IG4oKSkgJT4lCiAgICAgICAgc2VsZWN0KE1vbnRoLENSTV9JTlRFUkFDVElPTl9UWVBFLCBUb3RhbCkgJT4lIAogICAgICBnZ3Bsb3QoYWVzKGFzLmZhY3RvcihNb250aCksIFRvdGFsLCBncm91cD1DUk1fSU5URVJBQ1RJT05fVFlQRSwgY29sb3VyID0gQ1JNX0lOVEVSQUNUSU9OX1RZUEUpKSArIAogICAgICBnZW9tX2xpbmUoIGxpbmV3aWR0aD0xKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJNb250aCIsIHkgPSAiVG90YWwiLCBjb2xvcj0iQ1JNIEludGVyYWN0aW9uIFR5cGUiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkKYGBgCgoKIyMjIyBDUk0gSW50ZXJhY3Rpb24gYnkgRGF5IG9mIFdlZWsKYGBge3J9CmNybSAlPiUKZ3JvdXBfYnkoRE9XLCBDUk1fSU5URVJBQ1RJT05fVFlQRSkgJT4lCiAgICAgICAgc3VtbWFyaXNlKFRvdGFsID0gbigpKSAlPiUKICAgICAgICBzZWxlY3QoRE9XLENSTV9JTlRFUkFDVElPTl9UWVBFLCBUb3RhbCkgJT4lIAogICAgICBnZ3Bsb3QoYWVzKGFzLmZhY3RvcihET1cpLCBUb3RhbCwgZ3JvdXA9Q1JNX0lOVEVSQUNUSU9OX1RZUEUsIGNvbG91ciA9IENSTV9JTlRFUkFDVElPTl9UWVBFKSkgKyAKICAgICAgZ2VvbV9saW5lKCBsaW5ld2lkdGg9MSkgKyB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKHggPSAiRGF5IG9mIFdlZWsiLCB5ID0gIlRvdGFsIiwgY29sb3I9IkNSTSBJbnRlcmFjdGlvbiBUeXBlIikgKyAKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpCmBgYAojIyBHaWZ0IE92ZXJ2aWV3CgojIyMgR2lmdHMgb3ZlcnRpbWUKYGBge3J9CmdpZnQgPC0gZ2lmdCAlPiUKICAgICAgICBtdXRhdGUoWWVhciA9IGx1YnJpZGF0ZTo6eWVhcihHSUZUX0RBVEUpLAogICAgICAgICAgICAgICBRdWFydGVyID0gbHVicmlkYXRlOjpxdWFydGVyKEdJRlRfREFURSksCiAgICAgICAgICAgICAgIE1vbnRoID0gbHVicmlkYXRlOjptb250aChHSUZUX0RBVEUsIGxhYmVsID0gVFJVRSksCiAgICAgICAgICAgICAgIERPVyA9IGx1YnJpZGF0ZTo6d2RheShHSUZUX0RBVEUsIGxhYmVsPVRSVUUpKQpgYGAKCiMjIyMgR2lmdCBieSBZZWFyCmBgYHtyfQpnIDwtIGdpZnQgJT4lCmdyb3VwX2J5KFllYXIpICU+JQogICAgICAgIHN1bW1hcmlzZShUb3RhbCA9IHN1bShBTU9VTlQpKSAlPiUKICAgICAgICBzZWxlY3QoWWVhciwgVG90YWwpICU+JSAKICAgICAgICBuYS5vbWl0KCkgJT4lCiAgICAgIGdncGxvdChhZXMoWWVhciwgVG90YWwpKSArIAogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iix3aWR0aCA9IDAuNSwgZmlsbD0nYmxhY2snKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJUb3RhbCIpICsgCiAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogICAgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKZ2dwbG90bHkoZykKYGBgCgojIyMjIEdpZnQgYnkgUXVhcnRlcgpgYGB7cn0KZyA8LSBnaWZ0ICU+JQpncm91cF9ieShRdWFydGVyKSAlPiUKICAgICAgICBzdW1tYXJpc2UoVG90YWwgPSBzdW0oQU1PVU5UKSkgJT4lCiAgICAgICAgc2VsZWN0KFF1YXJ0ZXIsIFRvdGFsKSAlPiUgCiAgICAgICAgbmEub21pdCgpICU+JQogICAgICBnZ3Bsb3QoYWVzKFF1YXJ0ZXIsIFRvdGFsKSkgKyAKICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsd2lkdGggPSAwLjUsIGZpbGw9J2JsYWNrJykgKyB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKHggPSAiUXVhcnRlciIsIHkgPSAiVG90YWwiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkKZ2dwbG90bHkoZykKYGBgCgpHaWZ0IGJ5IE1vbnRoCmBgYHtyfQpnIDwtIGdpZnQgJT4lCmdyb3VwX2J5KE1vbnRoKSAlPiUKICAgICAgICBzdW1tYXJpc2UoVG90YWwgPSBzdW0oQU1PVU5UKSkgJT4lCiAgICAgICAgc2VsZWN0KE1vbnRoLCBUb3RhbCkgJT4lIAogICAgICAgIG5hLm9taXQoKSAlPiUKICAgICAgZ2dwbG90KGFlcyhNb250aCwgVG90YWwpKSArIAogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iix3aWR0aCA9IDAuNSwgZmlsbD0nYmxhY2snKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJNb250aCIsIHkgPSAiVG90YWwiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkKZ2dwbG90bHkoZykKYGBgCgpHaWZ0IGJ5IERheSBvZiBXZWVrCmBgYHtyfQpnIDwtIGdpZnQgJT4lCmdyb3VwX2J5KERPVykgJT4lCiAgICAgICAgc3VtbWFyaXNlKFRvdGFsID0gc3VtKEFNT1VOVCkpICU+JQogICAgICAgIHNlbGVjdChET1csIFRvdGFsKSAlPiUgCiAgICAgICAgbmEub21pdCgpICU+JQogICAgICBnZ3Bsb3QoYWVzKERPVywgVG90YWwpKSArIAogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iix3aWR0aCA9IDAuNSwgZmlsbD0nYmxhY2snKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJEYXkgb2YgV2VlayIsIHkgPSAiVG90YWwiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkKZ2dwbG90bHkoZykKYGBgCgojIyBWaWRlbyBPdmVydmlldwojIyMgVmlkZW8gVmlld3Mgb3ZlciB0aW1lCmBgYHtyfQp2aWRlbyA8LSB2aWRlbyAlPiUKICAgICAgICBtdXRhdGUoWWVhciA9IGx1YnJpZGF0ZTo6eWVhcihTRU5UX0RBVEUpLAogICAgICAgICAgICAgICBRdWFydGVyID0gbHVicmlkYXRlOjpxdWFydGVyKFNFTlRfREFURSksCiAgICAgICAgICAgICAgIE1vbnRoID0gbHVicmlkYXRlOjptb250aChTRU5UX0RBVEUsIGxhYmVsID0gVFJVRSksCiAgICAgICAgICAgICAgIERPVyA9IGx1YnJpZGF0ZTo6d2RheShTRU5UX0RBVEUsIGxhYmVsPVRSVUUpKQpgYGAKCiMjIyMgVmlkZW8gdmlld3MgYnkgeWVhcgpgYGB7cn0KZyA8LSB2aWRlbyAlPiUKZ3JvdXBfYnkoWWVhcikgJT4lCiAgICAgICAgc3VtbWFyaXNlKFRvdGFsID0gc3VtKFZJREVPX1ZJRVdTKSkgJT4lCiAgICAgICAgc2VsZWN0KFllYXIsIFRvdGFsKSAlPiUgCiAgICAgICAgbmEub21pdCgpICU+JQogICAgICBnZ3Bsb3QoYWVzKGFzLmZhY3RvcihZZWFyKSwgVG90YWwpKSArIAogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iix3aWR0aCA9IDAuNSwgZmlsbD0nYmxhY2snKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJUb3RhbCIpICsgCiAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogICAgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEpKQpnZ3Bsb3RseShnKQpgYGAKCiMjIyMgVmlkZW8gVmlld3MgYnkgUXVhcnRlcgpgYGB7cn0KZyA8LSB2aWRlbyAlPiUKZ3JvdXBfYnkoUXVhcnRlcikgJT4lCiAgICAgICAgc3VtbWFyaXNlKFRvdGFsID0gc3VtKFZJREVPX1ZJRVdTKSkgJT4lCiAgICAgICAgc2VsZWN0KFF1YXJ0ZXIsIFRvdGFsKSAlPiUgCiAgICAgICAgbmEub21pdCgpICU+JQogICAgICBnZ3Bsb3QoYWVzKFF1YXJ0ZXIsIFRvdGFsKSkgKyAKICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsd2lkdGggPSAwLjUsIGZpbGw9J2JsYWNrJykgKyB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKHggPSAiUXVhcnRlciIsIHkgPSAiVG90YWwiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkKZ2dwbG90bHkoZykKYGBgCgojIyMjIFZpZGVvIFZpZXdzIGJ5IE1vbnRoCmBgYHtyfQpnIDwtIHZpZGVvICU+JQpncm91cF9ieShNb250aCkgJT4lCiAgICAgICAgc3VtbWFyaXNlKFRvdGFsID0gc3VtKFZJREVPX1ZJRVdTKSkgJT4lCiAgICAgICAgc2VsZWN0KE1vbnRoLCBUb3RhbCkgJT4lIAogICAgICAgIG5hLm9taXQoKSAlPiUKICAgICAgZ2dwbG90KGFlcyhNb250aCwgVG90YWwpKSArIAogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iix3aWR0aCA9IDAuNSwgZmlsbD0nYmxhY2snKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJNb250aCIsIHkgPSAiVG90YWwiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkKZ2dwbG90bHkoZykKYGBgCgojIyMjIFZpZGVvIFZpZXdzIGJ5IERheSBvZiBXZWVrCmBgYHtyfQpnIDwtIHZpZGVvICU+JQpncm91cF9ieShET1cpICU+JQogICAgICAgIHN1bW1hcmlzZShUb3RhbCA9IHN1bShWSURFT19WSUVXUykpICU+JQogICAgICAgIHNlbGVjdChET1csIFRvdGFsKSAlPiUgCiAgICAgICAgbmEub21pdCgpICU+JQogICAgICBnZ3Bsb3QoYWVzKERPVywgVG90YWwpKSArIAogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iix3aWR0aCA9IDAuNSwgZmlsbD0nYmxhY2snKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJEYXkgb2YgdGhlIHdlZWsiLCB5ID0gIlRvdGFsIikgKyAKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpCmdncGxvdGx5KGcpCmBgYAoKIyMgRG9ub3IgJiBFbmdhZ2VtZW50IEluc2lnaHRzCgojIyMgRG9uYXRpb25zIGJ5IENSTSBJbnRlcmFjdGlvbiBUeXBlCmBgYHtyfQpsZWZ0X2pvaW4oZ2lmdCxjcm0sYnk9J0NPTlNUSVRVRU5UX0lEJykgJT4lCiAgZ3JvdXBfYnkoQ1JNX0lOVEVSQUNUSU9OX1RZUEUpICU+JQogIHN1bW1hcmlzZShUb3RhbCA9IHN1bShBTU9VTlQpKSAlPiUKICBzZWxlY3QoQ1JNX0lOVEVSQUNUSU9OX1RZUEUsVG90YWwpICU+JQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoQ1JNX0lOVEVSQUNUSU9OX1RZUEUsVG90YWwpICx5ID0gVG90YWwpKSAgKwogICAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLHdpZHRoID0gMC41LCBmaWxsPSdibGFjaycpICArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpICsKICAgICAgICBsYWJzKHggPSJDUk0gSW50ZXJhY3Rpb24gVHlwZSIsIHkgPSAiRG9uYXRpb25zIikgKyBjb29yZF9mbGlwKCkgKwogICAgICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKQoKCmBgYAoKIyBQYXJ0IDI6IEEgVGFsZSBvZiBQb3J0Zm9saW9zL0N1c3RvbWVyIFNlZ21lbnRhdGlvbgoKYGBgCgoKYGBge3J9CgpgYGAKCgpgYGB7cn0KCmBgYAoKCgoKCmBgYHtyfQoKYGBgCgoKYGBge3J9CgpgYGAKCgpgYGB7cn0KCmBgYAoKCmBgYHtyfQoKYGBgCgojIFBhcnQgMzogVGhlIFBhdGggRm9yd2FyZDogQWN0aXZhdGluZyBPdXIgU3RyYXRlZ3kKCmBgYHtyfQoKYGBgCgoKYGBge3J9CgpgYGAKCgpgYGB7cn0KCmBgYAoKCmBgYHtyfQoKYGBgCg==